#ifndef __POPUP_LIB_TYPES_H__
#define __POPUP_LIB_TYPES_H__

#include <string>
#include <vector>
#include <set>
#include <map>
#include <stdint.h>
#include <PopupOSAL.hpp>

#define POPUP_MSG_SPLIT_SIZE 1024

namespace Popup
{
#ifdef __GNUC__
#define __unused__ __attribute__ ((unused))
#endif

  typedef enum
  {
    POPUP_CONNECTION_SUCCESS,
    POPUP_CONNECTION_ERR_UNKNOWN,
    POPUP_CONNECTION_ERR_SERVER_DOWN,
    POPUP_CONNECTION_ERR_HANDSHAKE_FAILED,
    POPUP_CONNECTION_ERR_USER_ALREADY_CONNECTED,
    POPUP_CONNECTION_ERR_SQLITE,
    POPUP_CONNECTION_ERR_UNKNOWN_USER,
    POPUP_CONNECTION_ERR_INVALID_PASSWORD,
    POPUP_CONNECTION_ERR_EMPTY_LOGIN,
    POPUP_CONNECTION_ERR_LOGIN_ALREADY_EXISTS,
    POPUP_CONNECTION_ERR_LISTEN_LOOP_EXITED
  } ConnectionEcode;

  typedef enum
  {
    POPUP_MODE_MUTED = 0,
    POPUP_MODE_DEFAULT,
    POPUP_MODE_PROMPT,
    POPUP_MODE_TEXTONLY,
    POPUP_NB_MODES
  } UserMode;

  typedef enum {
    POPUP_USER_FIELD_NICKNAME        = 0x0001,
    POPUP_USER_FIELD_AVATAR          = 0x0002,
    POPUP_USER_FIELD_MODE            = 0x0004,
    POPUP_USER_FIELD_HEALTH          = 0x0008,
    POPUP_USER_FIELD_MAX             = POPUP_USER_FIELD_HEALTH,
    POPUP_USER_FIELD_ALL             = 0xFFFF
  } UserField;

  typedef enum {
    POPUP_RATE_UNKNOWN   = 0x0000,
    POPUP_RATE_LIKE      = 0x0001,
    POPUP_RATE_DONTLIKE  = 0x0002,
    POPUP_RATE_POETRY    = 0x0004,
    POPUP_RATE_FUCKYOU   = 0x0008,
    POPUP_RATE_ITEM_MAX  = POPUP_RATE_FUCKYOU
  } UserRateItem ;

  typedef unsigned long long /*uint64_t*/ ThreadID;
  typedef unsigned long long /*uint64_t*/  MessageID;
  typedef unsigned long long /*uint64_t*/  AttachmentID;
  typedef unsigned int /*uint32_t*/  UserID;
  typedef unsigned long long /*uint64_t*/  CanvassID;
  typedef unsigned int /*uint32_t*/ SessionID;

  struct User
  {
#define BAD_AVATAR   "badavatar"
#define BAD_LOGIN    "badlogin"
#define BAD_NICKNAME "badnickname"

    User()
    : id(-1), login(BAD_LOGIN), alive(false), nickname(BAD_NICKNAME),
      avatar(BAD_AVATAR), mode(POPUP_MODE_DEFAULT) {}

    User(const User & p_user)
    : id(p_user.getID()), login(p_user.getLogin()), alive(p_user.isAlive()),
      nickname(p_user.getNickname()), avatar(p_user.getAvatar()),
      mode(p_user.getMode()) {}

    virtual ~User() {}

    inline UserID getID() const                    { return id; }
    inline const std::string & getLogin() const    { return login; }
    inline const std::string & getNickname() const { return nickname; }
    inline const std::string & getAvatar() const   { return avatar; }
    inline UserMode getMode() const                { return mode; }
    inline bool isAlive() const                    { return alive; }

    inline void setNickname(const std::string & p_nickname)  { nickname = p_nickname; }
    inline void setAvatar(const std::string & p_avatar) { avatar = p_avatar; }
    inline void setMode(UserMode p_mode) { mode = p_mode; }
    inline void setAlive(bool p_isAlive) { alive = p_isAlive; }

    void update(const User & p_user, unsigned short p_updateMask);

    UserID id;
    std::string login;
    bool alive;
    std::string nickname;
    std::string avatar;
    UserMode mode;
    std::map<unsigned short, unsigned int> statistics;
    std::vector<unsigned short> records;
  };

  typedef std::set<UserID> UserList;
  typedef std::set<UserID> UserSet;
  typedef std::map<UserID, User*> UserMap;
  typedef std::map<std::string, User*> UserLoginMap;

  const UserList EmptyUserList;
  const User     NullUser;

  //============================================================================
  // Popup user message
  //============================================================================

  typedef std::vector<std::string> AttachmentList;

  struct UserMessage
  {
    UserMessage(const std::string & p_text = "",
                const std::string & p_title = "")
    : msgID(0), senderID(0), threadID(0), text(p_text), title(p_title)  {}
    UserMessage(const UserMessage & p_msg)
    : msgID(p_msg.msgID), senderID(p_msg.senderID), threadID(p_msg.threadID),
      targets(p_msg.targets), URLs(p_msg.URLs), files(p_msg.files),
      text(p_msg.text), title(p_msg.title) {}

    //! Message ID
    MessageID msgID;
    //! Sender
    UserID senderID;
    //! Thread ID
    ThreadID threadID;

    //! Receivers
    UserList targets;
    //! Attached URLs
    AttachmentList URLs;
    //! Attached files
    AttachmentList files;
    //! Text message
    std::string text;
    //! Title
    std::string title;
  };

  //============================================================================
  // Popup user message reaction (like, nice, ... )
  //============================================================================

  struct RateUserMessage
  {
    RateUserMessage(UserRateItem p_reaction = POPUP_RATE_UNKNOWN,
                        UserID p_reactionSenderID = 0,
                        const MessageID & p_sourceMessage = 0,
                        const ThreadID & p_threadID = 0,
                        UserID p_sourceMessageSender = 0,
                        const UserList & p_targets = EmptyUserList,
                        bool p_isUndo = false)
    : rateItem(p_reaction), rateSenderID(p_reactionSenderID),
      msgID(p_sourceMessage), threadID(p_threadID),
      msgSenderID(p_sourceMessageSender), targets(p_targets),
      isUndo(p_isUndo) {}
    RateUserMessage(const RateUserMessage & p_reaction)
    : rateItem(p_reaction.rateItem),
      rateSenderID(p_reaction.rateSenderID),
      msgID(p_reaction.msgID), threadID(p_reaction.threadID),
      msgSenderID(p_reaction.msgSenderID),
      targets(p_reaction.targets), isUndo(p_reaction.isUndo) {}

    UserRateItem rateItem;
    UserID rateSenderID;
    MessageID msgID;
    ThreadID threadID;
    UserID msgSenderID;
    UserList targets;
    bool isUndo;
  };

  //============================================================================
  // Thread Invitation
  //============================================================================

  struct ThreadInvitation
  {
    ThreadInvitation(const ThreadID & p_threadID = 0,
        const std::string & p_threadTitle = "")
    : threadID(p_threadID), threadTitle(p_threadTitle), inviter(0) {}

    // ID
    ThreadID threadID;
    // Title
    std::string threadTitle;
    // Invited users' IDs
    UserList invitedUsers;
    // Invitation sender (will be set automatically when message will be sent)
    UserID inviter;
    // CC users already in the thread
    UserList cc;
  };

  //============================================================================
  // Popup canvass
  //============================================================================

  typedef std::vector<std::string> ChoiceList;
  typedef std::vector<unsigned short> ChoiceIndexList;

  struct Canvass
  {
    Canvass() : canvassID(0), senderID(0), question(""), timeout(0),
        isAnonymous(false), allowMultiSelection(false) {}
    Canvass(const Canvass & p_canvass)
    : canvassID(p_canvass.canvassID), senderID(p_canvass.senderID),
      targets(p_canvass.targets), question(p_canvass.question),
      choices(p_canvass.choices), timeout(p_canvass.timeout),
      isAnonymous(p_canvass.isAnonymous),
      allowMultiSelection(p_canvass.allowMultiSelection) {}

    //! Canvass ID
    CanvassID canvassID;
    //! Sender
    UserID senderID;
    //! Receivers
    UserList targets;
    //! Question
    std::string question;
    //! Choices
    ChoiceList choices;
    //! Timeout
    unsigned short timeout;
    //! Anomymous
    bool isAnonymous;
    //! Multi selection allowed
    bool allowMultiSelection;
  };

  struct Vote
  {
    Vote() : canvassID(0), voterID(0) {}
    Vote(const Vote & p_vote)
    : canvassID(p_vote.canvassID), voterID(p_vote.voterID),
      choices(p_vote.choices) {}

    //! Canvass ID
    CanvassID canvassID;
    //! Voter
    UserID voterID;
    //! Selected choice index
    ChoiceIndexList choices;
  };

  //============================================================================
  // Transfer information
  //============================================================================

  typedef enum
  {
    POPUP_DIRECTION_IN,
    POPUP_DIRECTION_OUT
  } Direction;

  struct FileTransfer
  {
    Direction direction;
    PopupUtils::FileStream *file;
    AttachmentID attachmentID;
    MessageID messageID;
    std::string filepath;
    unsigned int totalSize;
    unsigned int nbTransferred;
    unsigned int nbRemaining;

    // Constructor used by a file receiver
    FileTransfer(Direction p_direction = POPUP_DIRECTION_IN,
                 MessageID p_messageID = 0,
                 AttachmentID p_attachmentID = 0,
                 const std::string & p_filepath = "",
                 PopupUtils::FileStream *p_file = 0,
                 unsigned int p_totalSize = 0)
     : direction(p_direction), file(p_file), attachmentID(p_attachmentID),
       messageID(p_messageID), filepath(p_filepath), totalSize(p_totalSize),
       nbTransferred(0), nbRemaining(p_totalSize) {}
    virtual ~FileTransfer() {}
  };

  //============================================================================
  // Statistics information
  //============================================================================

  struct UserStatistics : std::map<UserRateItem, unsigned int>
  {
    UserStatistics() : recordMask(0) {}

    void set(UserRateItem, unsigned int p_value, bool p_isRecord = false);
    bool get(UserRateItem p_item, unsigned int & p_value, bool & p_isRecord) const;
    bool isRecordman(UserRateItem p_item) const;
    unsigned short itemsMask() const;
    void dump() const;

    //! Bit field telling, for each item, if this is or not the record!
    unsigned short recordMask;
  };
}

#endif // __POPUP_LIB_TYPES_H__
